import axios, {
    type AxiosInstance,
    AxiosError,
    type AxiosRequestConfig,
    type AxiosResponse,
} from 'axios'

const baseURL: string = 'http://localhost:8000'

enum RequestEnums {
    TIMEOUT = 20000,
    FAIL = 5000, // 请求失败
    SUCCESS = 2000, // 请求成功
}

export interface ResultData<T> {
    code: number
    data: T
    msg: string
}

const config = {
    // 默认地址
    baseURL,
    // 设置超时时间
    timeout: RequestEnums.TIMEOUT as number,
    // 跨域时候允许携带凭证
    withCredentials: true,
}

class RequestHttp {
    // 定义成员变量并指定类型
    service: AxiosInstance
    public constructor(config: AxiosRequestConfig) {
        // 实例化axios
        this.service = axios.create(config)

        /**
         * 请求拦截器
         * 客户端发送请求 -> [请求拦截器] -> 服务器
         * token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
         */
        this.service.interceptors.request.use(
            (config: any) => {
                window.NLoading.show()

                const token = localStorage.getItem('token') || ''
                if (token && config.headers) {
                    typeof config.headers.set === 'function' &&
                        config.headers.set('Authorization', token)
                }
                return {
                    ...config,
                    headers: {
                        Authorization: token, // 请求头中携带token信息
                    },
                }
            },
            (error: AxiosError) => {
                // 请求报错
                Promise.reject(error)
            }
        )

        /**
         * 响应拦截器
         * 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
         */
        this.service.interceptors.response.use(
            (response: AxiosResponse) => {
                window.NLoading.hide()

                const { data } = response // 解构
                if (data.code === RequestEnums.SUCCESS || data.code === '2000') {
                    return Promise.resolve(data)
                }
                return Promise.reject(data)
            },
            (error: AxiosError) => {
                window.NLoading.hide()
                if (error.code === 'ERR_NETWORK') {
                    // 网络请求失败
                    window.NMessage.error('网络请求失败')
                }
                return Promise.reject(error.response)
            }
        )
    }

    // 常用方法封装
    get<T>(url: string, params?: object): Promise<ResultData<T>> {
        return this.service.get(url, { params })
    }
    post<T>(url: string, params?: object): Promise<ResultData<T>> {
        return this.service.post(url, params)
    }
    put<T>(url: string, params?: object): Promise<ResultData<T>> {
        return this.service.put(url, params)
    }
    delete<T>(url: string, params?: object): Promise<ResultData<T>> {
        return this.service.delete(url, { params })
    }
}

// 导出一个实例对象
export default new RequestHttp(config)
